package com.recursivechaos.gamemaster.dao;

/**
 * CHARACTER OBJECT DAO
 * Handles all interactions/functions with the character objects, abstracted away from the object
 * themselves.
 * @author Andrew Bell
 */

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.criterion.Restrictions;

import com.recursivechaos.gamemaster.classes.CharObj;
import com.recursivechaos.gamemaster.classes.RaceObj;
import com.recursivechaos.gamemaster.classes.WeaponObj;
import com.recursivechaos.gamemaster.exception.GameException;

public class CharObjDAO extends DAO {
	private WeaponObjDAO weapDAO = new WeaponObjDAO();
	private RaceObjDAO raceDAO = new RaceObjDAO();
	private MonsterNameDAO monDAO = new MonsterNameDAO();

	/**
	 * Adds kill to PC's stat
	 * 
	 * @param attackor
	 *            PC earning the kill
	 * @throws GameException
	 */
	public void addKill(CharObj attackor) throws GameException {
		attackor.setKills(attackor.getKills() + 1);
		update(attackor);
	}

	/**
	 * Find character object within a string
	 * 
	 * @param input
	 *            string containing the character
	 * @return Character, if found
	 * @throws GameException
	 */
	public CharObj findCharInString(String input) throws GameException {
		CharObj foundChar;
		input = input.toUpperCase();
		input = input.substring(input.indexOf(" ") + 1);
		if (input.contains(" ")) {
			input = input.substring(0, input.indexOf(" "));
		}
		String charString = "";
		List<String> charList = getCharStringList();
		for (int i = 0; i < charList.size(); i++) {
			String cname = charList.get(i);
			if (input.equalsIgnoreCase(cname)) {
				if (charString.equals("")) {
					charString = charList.get(i);
				} else {
					throw new GameException("Multiattack not allowed.");
				}
			}
		}
		if (charString.equals("")) {
			throw new GameException("That is not a registered player.");
		}
		// Once we've checked the input for a race, we get the object
		try {
			begin();
			foundChar = (CharObj) getSession().get(CharObj.class, charString);
			commit();
		} catch (HibernateException e) {
			rollback();
			throw new GameException("Could not get char details.");
		} finally {
			close();
		}

		return foundChar;
	}

	/**
	 * Returns character given a string
	 * 
	 * @param charname
	 *            String of the character to be found
	 * @return CharObj
	 * @throws GameException
	 *             If no character found
	 */
	public CharObj getChar(String charname) throws GameException {
		try {
			begin();
			CharObj foundChar = (CharObj) getSession().get(CharObj.class,
					charname);
			commit();
			if (foundChar == null) {
				throw new GameException("Could not get character.");
			}
			return foundChar;
		} catch (HibernateException e) {
			rollback();
			throw new GameException("Could not get character " + charname);
		} finally {
			close();
		}
	}

	/**
	 * Get a List<String> of available characters
	 * 
	 * @return List<String> of characters
	 */
	@SuppressWarnings("unchecked")
	public List<String> getCharStringList() {
		List<String> charList = null;
		try {
			begin();
			charList = getSession().createSQLQuery(
					"SELECT CHAR_NAME FROM recursi3_igm_db.charobjs;").list();
			commit();
		} catch (Exception e) {
			rollback();
		} finally {
			close();
		}
		return charList;
	}

	/**
	 * Returns a List<String> of current monsters (Typically, only one).
	 * 
	 * @return      List<String> of current monsters
	 * @throws GameException 
	 */
	@SuppressWarnings("unchecked")
	public List<String> getMonsterStringList() throws GameException {
		List<String> charList = null;
		try {
			begin();
			charList = getSession().createSQLQuery(
					"SELECT CHAR_NAME " + "FROM recursi3_igm_db.charobjs "
							+ "WHERE CHAR_TYPE='monster';").list();
			commit();
		} catch (Exception e) {
			rollback();
			throw new GameException("Was not able to fetch monster list: " + e.getMessage());
		} finally {
			close();
		}
		return charList;
	}

	/**
	 * Checks if character is a GM
	 * 
	 * @param sender
	 *            character to check
	 * @return true: Charater is GM
	 */
	public boolean isGM(String sender) {
		CharObj gmTest;
		try {
			gmTest = getChar(sender);
		} catch (GameException e) {
			return false;
		}
		if (gmTest.getIsGM() == 1) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isMonster(CharObj attackee) {
        return attackee.getType().equals("monster");

	}

	/**
	 * Returns status of if a monster is present in channel/campaign
	 * 
	 * @param channel
	 *            channel to check
	 * @return true if monster is present.
	 * @throws GameException
	 */
	public boolean isMonsterPresent(String channel) throws GameException {
		List<?> results;
		try {
			begin();
			Criteria crit = getSession().createCriteria(CharObj.class);
			crit.add(Restrictions.eq("type", "monster"));
			// crit.add(Restrictions.eq("campaign", thisCamp));
			results = crit.list();
			commit();
		} catch (Exception e) {
			throw new GameException("Could not check for monsters.");
		} finally {
			close();
		}
		if (results.size() > 0) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Checks to see if the current nick is registered.
	 * 
	 * @param sender    character to check
	 * @return true if char is registered
	 */
	public boolean isRegistered(String sender) {
		boolean regStatus = true;
		try {
			getChar(sender);
		} catch (Exception e) {
			regStatus = false;
		}
		return regStatus;
	}

	/**
	 * Registers a character with the game and saves them to the db
	 * 
	 * @param channel
	 *            Channel that the character is playing in (campaign)
	 * @param sender
	 *            Character Name
	 * @param input
	 *            Character creation string "<race>"
	 * @return CharObj, after creation and addition to the db
	 * @throws GameException
	 *             Either Character already exists, or other error
	 */
	public CharObj registerChar(String channel, String sender, String input)
			throws GameException {
		try {
			RaceObj race;
			try {
				race = raceDAO.findRaceInString(input);
			} catch (Exception e) {
				race = raceDAO.getRandomRace();
			}
			// try to find weapon, if not, generate random
			WeaponObj weapon;
			try {
				weapon = weapDAO.findWeaponInString(input);
			} catch (Exception e) {
				weapon = weapDAO.getRandomWeapon();
			}
			// create user, generate cur hp, save to db
			CharObj user = new CharObj(sender, race, weapon);
			user.setCurHp(user.getRace().getRaceHp());
			user.setConscious(1);
			user.setType("player");
			user.setKills(0);
			begin();
			getSession().save(user);
			commit();
			return user;
		} catch (HibernateException e) {
			rollback();
			throw new GameException("Could not create character" + sender);
		} finally {
			close();
		}
	}

	public void setRace(CharObj thisChar, String message) throws GameException {
		thisChar.setRace(raceDAO.findRaceInString(message));
		// Adjusts the HP accordingly
		int cur = thisChar.getCurHp();
		int race = thisChar.getRace().getRaceHp();
		if ((cur) > (race)) {
			thisChar.setCurHp(thisChar.getRace().getRaceHp());
		}
		update(thisChar);
	}

	public void setWeapon(CharObj thisChar, String message)
			throws GameException {
		thisChar.setWeapon(weapDAO.findWeaponInString(message));
		update(thisChar);

	}

	/**
	 * Returns a random monster object
	 * 
	 * @param message
	 *            Contains the channel/campaign to spawn in
	 * @return A CharObj
	 * @throws GameException
	 */
	public CharObj spawnRandomMonster(String message) throws GameException {
		String monName = monDAO.getRandomName();
		CharObj monster = registerChar(message, monName, message);
		RaceObj race = monster.getRace();
		race.setRaceAc(5);
		monster.setRace(race);
		monDAO.makeMonster(monster);
		return monster;
	}

	/**
	 * Applies damage to a character and updates db
	 * 
	 * @param attackee
	 *            char taking damage
	 * @param i
	 *            amount of hp worth
	 * @throws GameException
	 */
	public void takeDamage(CharObj attackee, int i) throws GameException {
		try {
			begin();
			attackee.setCurHp((attackee.getCurHp() - i));
			if (attackee.getCurHp() <= 0) {
				attackee.setConscious(0);
			}
			getSession().update(attackee);
			commit();
		} catch (Exception e) {
			rollback();
			throw new GameException("Was not able to update after attack.");
		} finally {
			close();
		}
	}

	/**
	 * Removes an character from the game
	 * 
	 * @param attackee
	 *            character to be removed
	 * @throws GameException
	 */
	public void unmake(CharObj attackee) throws GameException {
		try {
			begin();
			getSession().delete(attackee);
			commit();
		} catch (Exception e) {
			rollback();
			throw new GameException("Could not slay " + attackee.getCharName());
		} finally {
			close();
		}

	}

	/**
	 * updates a character once modified
	 * 
	 * @param thisChar
	 *            character to be modified
	 * @throws GameException
	 */
	private void update(CharObj thisChar) throws GameException {
		try {
			begin();
			getSession().update(thisChar);
			commit();
		} catch (Exception e) {
			rollback();
			throw new GameException("Was not able to update character.");
		} finally {
			close();
		}
	}
}
